package com.citi.gts.galto.modules.validation.rule.impl;

import com.citi.gts.galto.modules.validation.Precondition;
import com.citi.gts.galto.modules.validation.exception.SubServiceException;
import com.citi.gts.galto.modules.validation.exception.ValidationException;
import com.citi.gts.galto.modules.validation.rule.Verifiable;
import com.citi.gts.galto.modules.validation.vo.SampleInstruction;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.Method;
import java.util.*;

public final class CompositeRule implements Verifiable, ApplicationContextAware {

    private List<Verifiable> childRuleList;

    private ApplicationContext context;

    @Override
    public void validate(SampleInstruction instruction) throws SubServiceException, ValidationException {

        Set<String> executedRulesSet = new LinkedHashSet<String>();

        for (Verifiable rule : childRuleList) {

            checkPreconditions(rule, instruction, executedRulesSet);

        }

    }

    private boolean hasPrecondition(Verifiable rule) {
        Class<? extends Verifiable> ruleClass = rule.getClass();

        Method validateMethod = null;

        try {

            validateMethod = ruleClass.getMethod("validate", SampleInstruction.class);

        } catch (NoSuchMethodException e) {
            return false;
        }

        return validateMethod.isAnnotationPresent(Precondition.class);
    }

    private void checkPreconditions(Verifiable rule, SampleInstruction instruction, Set<String> executedRulesSet) throws SubServiceException, ValidationException {

        try {

            Class<? extends Verifiable> ruleClass = rule.getClass();

            Method validateMethod = ruleClass.getMethod("validate", SampleInstruction.class);

            if (hasPrecondition(rule)) {

                Precondition precondition = validateMethod.getAnnotation(Precondition.class);

                for (String beanRule : precondition.rule()) {

                    if (executedRulesSet.contains(beanRule)) {
                        continue;
                    }

                    Verifiable preConditionRule = (Verifiable) context.getBean(beanRule);

                    checkPreconditions(preConditionRule, instruction, executedRulesSet);

                    executedRulesSet.add(beanRule);

                    System.out.println("Executed rules: " + executedRulesSet);

                }

                rule.validate(instruction);

            } else {

                rule.validate(instruction);

            }

        } catch (NoSuchMethodException ignore) {
        }
    }

    @Required
    public void setChildRuleList(List<Verifiable> childRuleList) {
        this.childRuleList = childRuleList;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("CompositeRule");
        sb.append('{').append(childRuleList);
        sb.append('}');
        return sb.toString();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }
}